Automatic Card Shuffler & Dealer

Continous shuffles and customizable dealing machine
Ruirong Huang (rh539) & Jiajun Sun (js2969)
05/21/2022


Demonstration Video


Introduction

We found that most of the shuffler on the market can only achieve one-time shuffle. What we planned to achieve is continuous shuffles, where we can shuffle multiple times. Besides, we planned to combine the shuffler with a dealer so that after the shuffles, one can deal the cards according to the pre-defined games or define one's own game.

We achieved the continuous shuffles by a scissor lift and an arm. After each shuffle, the scissor lift would lift up the deck and the arm would cut the deck into half and swing them to each sides for that they are ready for the next shuffle.

We use a Raspberry Pi and the PiTFT for the control and the GUI of the program. We provide Texas Hold'em, Fight the Landlord, Showhand as the pre-defined games. For user-defined mode, one can customize the player number, the public cards number, and cards per person.

Generic placeholder image

Whole Structure of the Machine


Project Objective:

  • Achieve continuous shuffles of the user defined times
  • Deal the cards according to pre-defined or user-defined card games
  • Draw the GUI for users to customize the games

Design & Testing

the GUI

As defined, our project is a cards shuffling and dealing machine. Therefore, the GUI should has the functionality to let users easily choose how to shuffle and how to deal the cards. Following this idea, we decided to build several interfaces, including the main interface, the mode choosing interface, and so on. In the main interface, the screen is split into two parts: left and right. The left part is used to show the number of shuffling times the user wants (by default 2). The user can press the minus and add button around the number to adjust the shuffling times. On the right side, there is a button named Mode at up right and an information displaying part to show the information about the current mode at down right. By default, the mode is None, which means we don't need the machine to deal cards, instead we just need it to shuffle. If the user presses the Mode button, the GUI will switch into a second interface: the mode choosing interface. In this page, there is a list of games to choose, for example, Texas Hold'em, Show Hand, Fight the Landlord, Shuffle Only, and User Defined. As known to all, Texas Hold'em has five public cards and two cards per player. Therefore, the user only has to choose the number of players. However, in User Defined mode, the user needs to define all three features of the game: number of public cards, number of players and number of cards per player. And in the Shuffle Only mode, there is nothing to choose because this mode is used to just shuffle but not dealing. After setting the game mode, the GUI will automatically switch back to the main menu and the information of the current game mode will be shown at the down right of the screen. Now, the user can press the START button in the middle of the screen to start the whole shuffling and dealing procedure. To quit the program, the user can also press the QUIT button at the central button of the screen.

Generic placeholder image

The Python GUI of the project

Scissor Lift

The reason we need scissor lift is that we want to move a deck of cards vertically. In this way, it could receive cards from the shuffler at a lower altitude and then be lifted up so that the rotating arm could split the cards into two half decks back to the shuffler. The main part of the scissor lift came from a toy car we bought from Amazon. By rotating a circular disk clockwise on the side, it will cause the scissor lift to lift up. Similarly, by rotating it counter-clockwise, the scissor lift will gradually drop down. Therefore, we decided to use a servo and a pair of gears to rotate the circular disk to control the up and down movement of the scissor lift. To achieve this design, we decided to attach the servo at the bottom of the scissor lift and glued them together. And then, we used screws to attach two gears, which we have chosen carefully so that these two gears could bite together according to the distance between the center of the circular disk and the servo, with the circular disk and the servo respectively. Then, after powering the servo and tested the whole system, we found that when the scissor lift was lifting up, the circular disk was able to slide in and out so that the two gears were no longer biting together. Therefore, we decided to add a baffle outside these two gears so that they must be in one plane vertically. To make this baffle as smooth as possible, we used a small plastic baffle from the car given in Lab3 and two screws to attach it to the surface of the scissor lift. Another problem we have met later was that we found that the servo, as attached at the bottom of the scissor lift, was still not so precise vertically due to the gravity so that two gears were not biting perfectly. This resulted in that when a full deck of cards were placed at the top of the scissor lift, the gravity might cause the scissor lift to stop moving and two gears sliding. To solve this issue, we decided to add a supporting card board between the servo and the ground of our machine so that the vertical position of the servo could be maintained.

Generic placeholder image

Scissor Lift

the Relay

As the shuffle was also bought from Amazon, it was designed to power by four LR14/C batteries, which were much more powerful than the circuits we used to control servos and motors. In the original shuffler, the user has to press a physical button so that two electrical lines inside would be connected and the shuffler would start to work. As we want the whole machine to be controlled by our Raspberry Pi, we need to find a way to control the on and off of this shuffler by GPIOs and circuits. After discussing with the professor, we were told that there was a way to solve this problem, that is, to use a relay. We could send signals through GPIOs to a relay and connect the circuit of the shuffler into the two ports of the relay and it will control the on and off of the shuffler circuit. However, after we tried to test the relay and the shuffler, we found a weird phenomenon. That is, when the relay was on and the motors in the shuffler were working, the servo beside, which was not supposed to rotate, started to rotating. The servo, although was powered through the power line and the ground line, had no input from the signal line as its signal line was even not connected to any GPIO. This phenomena was so strange that we firstly thought that it might be because that the relay was not that perfect and the strong current in the shuffler circuit started to affect our main circuit. However, after testing different relays and further checking the relay circuit, we were sure that the relay worked fine and our circuit was connected properly. Just at the moment we started to feel despair, we tried to connect the shuffler circuit directly without the relay, and when the shuffler was working, the same phenomena appeared again: the servo was rotating. As now, the circuit of the shuffler had nothing to do with the main circuit, we finally realized that this weird thing might resulted from electromagnetic induction. To solve this issue, we came up with an idea to connect the signal line of the servo to a GPIO and manually set that GPIO to 0 (low voltage). After doing this, the servo was no longer rotating when the shuffler was working and the problem was solved.

Generic placeholder image

The Relay Set for Controlling the Shuffler

cards splitting mechanism

As the shuffler needs two half decks of cards placed at the top to shuffle them into one deck, we need to split that one whole deck back into two halves to achieve our goal of multiple-time shuffling. Therefore, we designed to use a rod, which was restricted in a groove to move horizontally and smoothly, to push half of the deck to the left or right while moving to the left and right correspondingly. And then, the question became how to power this rod so that it could move horizontally. Because we had servos and motors, and their moving pattern is rotating rather than sliding, we need to find out a mechanical way to convert this rotating movement into the sliding movement needed for the rod. In this step, we have came up with several ideas, including using ropes to grab the rod, using a gear groove to convert the movement, and so on. Finally, due to the simplicity and aesthetics, we chose to use a robot arm to connect the rod and the servo. When the servo was rotating, it would rotate the robot arm, and with another groove on the robot arm, the rod be pushed by the robot arm while moving horizontally. In this way, the servo could be places at the very bottom of the machine so that the upper space could be saved for the scissor lift. Initially, we used a motor to directly power the robot arm, but later when testing, we found that the friction from the cards and rods were too large for the power of a motor, especially taking consider of the long robot arm that would weaken the strength from the motor to the rod on the top. Therefore, we decided to use a servo instead of a motor, as it was more powerful. What’s more, we decided to add a gear set instead of directly letting the servo rotate the robot arm to slow the speed and strengthen the power. We also carefully measured the size of servos and the space inside the shuffler, so that the robot arm servo, the gear set, and the servo for scissor lift could be placed as compatible as possible to save space for the scissor lift. And then, we tested again and fortunately found that the strength was large enough to push the rod.

The next step, then, became how to control the scissor lift to lift to a precise position (same altitude as the rod) so that all cards could be split and no card would be left on the scissor lift. Initially, we decided to add two baffle just above the rod on each side of the cards to restrict the upper half of the deck so that when the rod moves, the lower half of the deck would be pushed to one side and the upper half would drop down due to gravity. And then, by moving the rod back, the upper half of the deck would be pushed to the other direction. However, after testing, we found that the friction from the gravity of the upper half was still too large and it was almost impossible to control the lifting altitude to be exactly the same every time. Therefore, we came up with another idea, that was, to lift the cards to about half above the bottom plane of the rod to let the rod just push the upper half of cards to one direction and then lift it upper to let the rod push the lower half to the other direction. However, after testing, we also found some issue. When the rod was moving, the friction between its bottom and the surface of the lower half deck of cards would cause one or several cards to be driven into that direction but not completely pushed out, which resulted that the scissor lift could no longer move higher as the lower half deck was now blocked by these cards. To solve this issue, we then came up with another idea. We decided to use two cards vertically attached on two sides of the well for the scissor lift inside the shuffler. The top of these two cards were curved inside so that when the deck of cards was lifted up, these curved parts would be straightened but when the scissor lift was coming down, these two curved parts would be curved back and those cards that were higher than these two curved cards would be supported by these two cards, while the lower deck of cards would be brought down. In this way, there would be no cards attached to the bottom of the rod and those upper parts of the deck could be smoothly and completely pushed out to one side. Therefore, the whole cards splitting procedure became: firstly lift up the deck of cards to about half above the bottom of the rod; secondly lift down the scissor lift so that the upper half of the deck would stay at the top and the lower half would be brought down with the scissor lift; thirdly drive the rod to push the upper half deck to one side; fourthly lift up the lower half deck high enough above the two curved cards; fifthly lift down so that all remaining cards are left supported by these two curved cards; finally drive the rod and push the remaining cards to the other side. We were excited that this mechanism theoretically solved all issues we have met, and fortunately after testing, it worked fine: the cards could now be split clearly. The only thing remaining was to carefully measure the time the servo needs to rotate for lifting up and down the scissor lift.

Generic placeholder image

The Arm for Cutting the deck

Generic placeholder image

The Two Curved Cards for holding the half deck

Dealing

For the dealing action we imitated the bought shuffler, how it uses one motor to rub the bottom card out. Since we use the DC motor, we added two wheels on both sides so that they increase the friction. We found that the scissor lift is hard to be lifted to a fixed height. To deal at a fixed place, after the shuffle, we drop it to the lowest place, which is the only fixed position of the platform holding the cards. We added a card in the front of the whole machine, then there exists a finedraw between the platform and the card’s end, so that only one card would be dealt at a time.

At first, we tried uniform rotation speed, but the result is not so good. The cards wouldn’t always come out one at a time. To fix this, we tried several ways. For example, discrete rotations or accelerated rotations. The conclusion is that accelerated rotations can help to separate the cards. Therefore, we split the dealing process in two parts, the first part the motor rotates at half duty cycle, and then at full speed. In this way the card can be dealt out, and the next card would stick out a little bit. An important thing to notice is that, the first card dealt needs more power to run past the barrier, so that we programmed to rotate the motor longer for the first card.

Generic placeholder image

The Motor for Dealing

Result

Overall, we accomplished our goal listed in the proposal. We achieved the continuous shuffles, so that the user can decide how many times they want to shuffle. We also managed to deal the cards according to pre-defined or user-defined card games. A GUI is drawn for users to customize the games whatever they want.

The project was much more complicated than we expected, mainly due to the mechanical structure we designed. Totally we used three servos, a motor and many gear sets. We worked for a very long time to manage to fit everything inside the shuffler and make sure they do not interfere with each other. Besides, it also took a long time for trial and error, such as determining the rotation time for the scissor lift and the swinging arm.

The most important structure came up only two days before the demo day. We decided to use the two curved cars for holding the half deck and lower the lift a little bit before cutting. This method really solved the friction problem but also took us much time to recreate the whole structure.


Future Work

Due to the limited time and budget, most of our parts are created using the paperboards, ice cream sticks and cards, glued or taped together. If possible, using 3D printed parts would increase the stability and aesthetic of the whole machine.

Another possible improvement is to design the whole structure with a more consistent powering system. Currently the shuffler is powered by a set of batteries, the motors are powered by another set of batteries, and the Raspberry Pi is powered by the power adapter. Since the batteries run out fast, the voltage often drops during time. It would be better to power the whole system on a fixed voltage, with one single wire sticking out.

At last, in order to demo the inner structure, everything is exposed. A shell should be designed to cover all inner parts, leaving the PiTFT out as a display screen. The shell should also provide a platform to ensure that the shuffling machine rotates horizontally.


Work Distribution

Generic placeholder image

Project group picture with Joe

Generic placeholder image

Ruirong Huang

rh539@cornell.edu

Always worked together so literally did half of the work

Generic placeholder image

Jiajun Sun

js2969@cornell.edu

Always worked together so literally did the other half of the work


Parts List & Budgets

Total: $91.65


References

Previous Student Projects (21SP)
Continuous Rotation Servo Datasheet
Raspberry Pi Cookbook
Pigpio Library
PWM for Motor

Code Appendix


#card_GUI.py
import pygame 
from pygame.locals import * # for event MOUSE variables
import RPi.GPIO as GPIO
import time
import os

# shuffle time
shuffle_t = 4

#for servo
low = 0.02
high_stop = 0.0015
high_clk = 0.0001
high_cnt = 0.0029

#for dc motor
dc_freq = 50
dc_full = 100
dc_half = 60
dc_stop = 0

#pwm & relay pins for the servos 
whole_rotate_pwm_GPIO = 16
lift_pwm_GPIO = 6
arm_pwm_GPIO = 5
deal_motor_pwm_pin = 19
deal_motor_dir1_pin = 20
deal_motor_dir2_pin = 21
relay_pin = 26

#the up/down routine time for the scissor lift
up_t1 = 3.45
up_t2 = 4.6
down_t1 = 3.3
down_t2 = 4.15
arm_t = 0.32

#time defined for dealing and rotation
deal_singer_card_time = 0.43
rotate_whole_time = 1

#all GPIO out pins
GPIO_out = [whole_rotate_pwm_GPIO, 
            relay_pin, 
            lift_pwm_GPIO, 
            arm_pwm_GPIO,
            deal_motor_pwm_pin,
            deal_motor_dir1_pin,
            deal_motor_dir2_pin
            ]

#initialize all the GPIO out pins
def GPIO_out_setup(GPIO_out):
    for GPIO_pin in GPIO_out:
        GPIO.setup(GPIO_pin, GPIO.OUT)
        GPIO.output(GPIO_pin, GPIO.LOW)

#use to calculate the frequency of the servo
def freq(low=low, high=high_stop):
    freq = 1/(low + high)
    print("low = {}, high = {}, freq = {}".format(low, high, freq))
    return freq
    # return 50

#the function to determine the duty cycle for the servo
def dc(low=low, high=high_stop):
    dc = high/(low + high) * 100
    print("low = {}, high = {}, dc = {}".format(low, high, dc))
    return dc

#the function to control the servo to rotate for a certain time
def servo_rotate(pwm_GPIO, rotate_time, direction):
    p = GPIO.PWM(pwm_GPIO, freq())
    p.start(dc())
    p.ChangeFrequency(freq(high=direction))
    p.ChangeDutyCycle(dc(high=direction))
    time.sleep(rotate_time)
    GPIO.output(pwm_GPIO, GPIO.LOW)
    p.stop()

#the arm routine defined by which way to start, whether odd moves
#if is_l_first is true, swing to the left firs#t
#if is_change is true, the end position is different from original position
def arm(arm_pwm_GPIO, t_mov, t_stop, n_iter, rate, is_l_first, is_change):
    t_temp = t_mov
    if is_l_first:
        for i in range(n_iter):
            servo_rotate(arm_pwm_GPIO, t_temp, high_clk)
            time.sleep(t_stop)
            servo_rotate(arm_pwm_GPIO, t_temp, high_cnt)
            time.sleep(t_stop)
            t_temp -= rate
        if is_change:
            servo_rotate(arm_pwm_GPIO, t_temp, high_clk)
    else:
        for i in range(n_iter):
            servo_rotate(arm_pwm_GPIO, t_temp, high_cnt)
            time.sleep(t_stop)
            servo_rotate(arm_pwm_GPIO, t_temp, high_clk)
            time.sleep(t_stop)
            t_temp -= rate
        if is_change:
            servo_rotate(arm_pwm_GPIO, t_temp, high_cnt)

#the function to control the dealing motor
#num_cards means the cards we would like to deal
# is_first is to determine whether it is the first time dealing cards
def motor_control(direction, num_cards, is_first):
    if direction:
        print("counter clockwise")
        GPIO.output(deal_motor_dir1_pin, GPIO.LOW)
        GPIO.output(deal_motor_dir2_pin, GPIO.HIGH)
    else:
        print("clockwise")
        GPIO.output(deal_motor_dir1_pin, GPIO.HIGH)
        GPIO.output(deal_motor_dir2_pin, GPIO.LOW)
    p = GPIO.PWM(deal_motor_pwm_pin, dc_freq)
    if is_first:
        p.start(dc_full)
        time.sleep(1*deal_singer_card_time/2)
        p.stop()
        time.sleep(0.7)
    for i in range(num_cards):
        p.start(dc_half)
        print("full speed")
        time.sleep(2*deal_singer_card_time/5)
        p.ChangeDutyCycle(dc_full)
        time.sleep(3*deal_singer_card_time/5)
        p.stop()
        time.sleep(0.7)

#the function to control the lift to cut the deck to two halves
def lift_control():
    # 1st half
    # up
    servo_rotate(lift_pwm_GPIO, up_t1, high_cnt)
    time.sleep(1)
    # down to botton
    servo_rotate(lift_pwm_GPIO, down_t1, high_clk)
    # arm left 1
    arm(arm_pwm_GPIO, arm_t, 0.7, 1, 0, 1, 1)

    # 2nd half
    # up
    servo_rotate(lift_pwm_GPIO, up_t2, high_cnt)
    time.sleep(1)
    # down to bottom
    servo_rotate(lift_pwm_GPIO, down_t2, high_clk)
    # right 1
    arm(arm_pwm_GPIO, arm_t, 0.7, 1, 0, 0, 1)

#the function to deal, first public cards then player's cards
def deal(players, public_cards, cards_per_person):
    motor_control(0, public_cards, 1)
    time.sleep(5)
    whole_each_rotate_time = rotate_whole_time/players

    motor_control(0, cards_per_person, 0)
    for j in range(players - 1):
        servo_rotate(whole_rotate_pwm_GPIO, whole_each_rotate_time, high_cnt)
        time.sleep(1)
        motor_control(0, cards_per_person, 0)
    time.sleep(2)
    for j in range(players - 1):
        servo_rotate(whole_rotate_pwm_GPIO, whole_each_rotate_time * 9 / 10, high_clk)
        time.sleep(0.5)
        


#controlling the relay pin for the shuffle 
def shuffle():
    GPIO.output(relay_pin, GPIO.LOW)
    # time.sleep(1)
    for i in range(5):
        GPIO.output(relay_pin, GPIO.HIGH)
        time.sleep(shuffle_t/5)
        GPIO.output(relay_pin, GPIO.LOW)
        time.sleep(0.2)
    # time.sleep(1)

# display function for the texts at main menu
def display(display_l):
    for i in display_l:
        if i[0] == title:
            text_surface = my_font_large.render(i[0], True, LIGHT_BLUE)
        elif i[0] == str_shuffle or i[0] == str_choose:
            text_surface = my_font.render(i[0], True, MINT)
        elif i[0] == str_start:
            text_surface = my_font_small.render(i[0], True, WHITE)
        elif i[0] == str_add or i[0] == str_minus or i[0] == str_s_time or i[0] == str(shuffle_time):
            text_surface = my_font_large.render(i[0], True, LIGHT_YELLOW)
        # elif i[0] == str_FTL or i[0] == str_texas:
        #     text_surface = my_font_small.render(i[0], True, WHITE)
        elif i[0] == str_quit:
            text_surface = my_font.render(i[0], True, WHITE)
        else:
            text_surface = my_font_small.render(i[0], True, WHITE)
        rect = text_surface.get_rect(center=i[1])
        if len(i) == 2:
            i.append(rect)
        elif len(i) == 3:
            i[2] = rect
        else:
            print("wrong button length")
        screen.blit(text_surface, rect)
    pygame.display.flip()

#display the choices
def choose_display(display_l):
    for i in display_l:
        if  i[0] == str_choose_title:
            text_surface = my_font_large.render(i[0], True, LIGHT_BLUE)
        else:
            text_surface = my_font.render(i[0], True, WHITE)
        rect = text_surface.get_rect(center=i[1])
        if len(i) == 2:
            i.append(rect)
        elif len(i) == 3:
            i[2] = rect
        else:
            print("wrong button length")
        screen.blit(text_surface, rect)
    pygame.display.flip()

#display the Texas Hold'em
def texas_display(display_l):
    for i in display_l:
        if  i[0] == texas_title:
            text_surface = my_font_xl.render(i[0], True, LIGHT_BLUE)
        elif i[0] == str_add or i[0] == str_minus or i[0] == str_players_number or i[0] == str(players):
            text_surface = my_font_xl.render(i[0], True, LIGHT_YELLOW)
        else:
            text_surface = my_font_xl.render(i[0], True, WHITE)
        rect = text_surface.get_rect(center=i[1])
        if len(i) == 2:
            i.append(rect)
        elif len(i) == 3:
            i[2] = rect
        else:
            print("wrong button length")
        screen.blit(text_surface, rect)
    pygame.display.flip()


def calculate_max_cards():
    deck = 52
    return int((deck - public_cards)/players)

#display the user defined interface
def usr_define_display(display_l):
    for i in display_l:
        if  i[0] == str_usr_define:
            text_surface = my_font_xl.render(i[0], True, LIGHT_BLUE)
        elif i[0] == str_players or i[0] == str_public_cards or i[0] == str_cards_per_person:
            text_surface = my_font.render(i[0], True, WHITE)
        elif i[0] == str_set:
            text_surface = my_font_xl.render(i[0], True, WHITE)
        elif i[0] == str_whole_deck:
            text_surface = my_font_small.render(i[0], True, MINT)
        else:
            text_surface = my_font_large.render(i[0], True, LIGHT_YELLOW)
        rect = text_surface.get_rect(center=i[1])
        if len(i) == 2:
            i.append(rect)
        elif len(i) == 3:
            i[2] = rect
        else:
            print("wrong button length")
        screen.blit(text_surface, rect)
    pygame.display.flip()

def start_display(display_l):
    for i in display_l:
        if i[0] == title:
            text_surface = my_font_large.render(i[0], True, LIGHT_BLUE)
        elif i[0] == str_shuffling :
            text_surface = my_font_xl.render(i[0], True, MINT)
        else:
            text_surface = my_font.render(i[0], True, WHITE)
        rect = text_surface.get_rect(center=i[1])
        if len(i) == 2:
            i.append(rect)
        elif len(i) == 3:
            i[2] = rect
        else:
            print("wrong button length")
        screen.blit(text_surface, rect)
    pygame.display.flip()

def rank_string(times):
    if(times%10 == 1):
        return "st"
    elif(times%10 == 2):
        return "nd"
    elif(times%10 == 3):
        return "rd"
    else:
        return "th"

# time used for history
t_start = time.time()
####

# set physical buttons
GPIO.setmode(GPIO.BCM)
GPIO_out_setup(GPIO_out)

#set up a bail out button
quit_buttons = 27
GPIO.setup(quit_buttons, GPIO.IN, pull_up_down=GPIO.PUD_UP)


def GPIO27_callback(channel):
    global is_quit
    print ("Button " + str(27) + " has been pressed")
    is_quit = True

GPIO.add_event_detect(27, GPIO.FALLING, callback = GPIO27_callback, bouncetime = 300)



# draw a circle
def draw_circle(color, position, radius):
    pygame.draw.circle(screen, color, position, radius)

def draw_rect(color, pos, width, height, edge):
    pygame.draw.rect(screen, color, Rect(pos[0]- width/2, pos[1] - height/2, width, height), edge)

####

# pygame init
os.putenv('SDL_VIDEODRIVER', 'fbcon') # Display on piTFT
os.putenv('SDL_FBDEV', '/dev/fb0') 
os.putenv('SDL_MOUSEDRV', 'TSLIB') # Track mouse clicks on piTFT 
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')
pygame.init()

pygame.mouse.set_visible(False)
WHITE = 255, 255, 255
BLACK = 0, 0, 0
RED = 255, 0, 0
GREEN = 0, 255, 0
BLUE = 0, 0, 255
MINT = 81, 224, 100
LIGHT_YELLOW = 247, 241, 129
LIGHT_BLUE = 65, 224, 244
DARK_BLUE = 29, 44, 143
LIGHT_ORANGE = 235, 141, 40
red_green_l = [RED, GREEN]
screen = pygame.display.set_mode((320, 240))
my_font = pygame.font.Font(None, 30)
my_font_small = pygame.font.Font(None, 23)
my_font_large = pygame.font.Font(None, 35)
my_font_xl = pygame.font.Font(None, 45)
####


# several kinds of messages
# positions
pos_title = (160, 20)
pos_shuffle = (70, 90)
pos_minus = (35, 160)
pos_add = (105, 160)
pos_choose = (250, 90)
pos_quit = (160, 200)
pos_start = (160, 120)
pos_mode = (250, 160)
pos_underline = (250, 170)
pos_players_main = (250, 190)
pos_public_main = (250, 205)
pos_cards_main = (250, 220)
pos_s_time = (70, 160)

#5 modes
pos_1 = (160,60)
pos_2 = (160,100)
pos_3 = (160,140)
pos_4 = (160,180)
pos_5 = (160,220)

#texas choose player
pos_texas_minus = (100,140)
pos_texas_add = (220,140)
pos_players_texas = (160,80)
pos_set = (160,200)

#user_define
pos_players_user= (85,60)
pos_public_cards_user = (85,100)
pos_cards_per_person_user = (85,140)
pos_minus_user_1 = (200, 60)
pos_add_user_1 = (280, 60)
pos_minus_user_2 = (200, 100)
pos_add_user_2 = (280, 100)
pos_minus_user_3 = (200, 140)
pos_add_user_3 = (280, 140)
pos_players_user_number = (240,60)
pos_public_cards_user_number  = (240,100)
pos_cards_per_person_user_number  = (240,140)
pos_whole_deck  = (240,180)


pos_current_shuffle = (110,170)
pos_current_shuffle_number = (210,170)
pos_back = (160, 200)

# strings
title = "Time To Play Some Cards!" #0
str_shuffle = "Shuffle" #1
str_minus = "-" #2
str_add  = "+" #3
str_choose  = "Mode" #4
str_quit = "QUIT" #5
str_start = "START!" #6
str_mode_none = "None" #7
str_underline = "—————" #8
str_s_time = "2" #9
str_show_set_players = ""#10
str_show_set_public = ""#11
str_show_set_cards = ""#12

str_choose_title = "Choose Game Mode" #0
#add your game here
str_texas = "Texas Hold'em" #1
str_FTL = "Fight the Landlore" #2
str_showhand = "Show Hand" #3
str_usr_define = "User-Defined" #4
str_shuffle_only = "Shuffle_Only" #5

texas_title = "Texas Hold'em" #0
str_players = "Players" #1
str_players_number = "1" #2
str_set = "SET" #3

str_public_cards = "Public Cards"
str_cards_per_person = "Cards per person"
str_public_cards_number = "0"
str_cards_per_person_number = "0"
str_whole_deck = "Whole Deck"

str_shuffling = "Shuffling……"
str_current = "Current: "
str_current_shuffle_number = "1st shuffle"
str_dealing = "Dealing……"
str_deal_finish = "Dealing Finished!"
str_back = "BACK"



# initial display list
display_l = [[title, pos_title], #1
             [str_shuffle, pos_shuffle],#2
             [str_minus, pos_minus],#3
             [str_add, pos_add],#4
             [str_choose, pos_choose],#5
             [str_quit, pos_quit],#6
             [str_start, pos_start],#7
             [str_mode_none, pos_mode],#8
             [str_underline, pos_underline],#9
             [str_s_time, pos_s_time],#10
             [str_show_set_players, pos_players_main],#11
             [str_show_set_public, pos_public_main],#12
             [str_show_set_cards, pos_cards_main],#13

]

choose_display_l = [[str_choose_title, pos_title], 
                    [str_texas, pos_1],
                    [str_FTL, pos_2],
                    [str_showhand, pos_3],
                    [str_usr_define, pos_4],
                    [str_shuffle_only, pos_5]
]


texas_display_l = [ [texas_title, pos_title],
                    [str_players, pos_players_texas],
                    [str_players_number, pos_3],
                    [str_set, pos_set],
                    [str_minus, pos_texas_minus],
                    [str_add, pos_texas_add],
]

showhand_display_l = [ [str_showhand, pos_title],
                    [str_players, pos_players_texas],
                    [str_players_number, pos_3],
                    [str_set, pos_set],
                    [str_minus, pos_texas_minus],
                    [str_add, pos_texas_add],
]


user_define_display_l = [[str_usr_define, pos_title], #0
                        [str_players, pos_players_user], #1
                        [str_public_cards, pos_public_cards_user], #2
                        [str_cards_per_person, pos_cards_per_person_user], #3
                        [str_minus, pos_minus_user_1], #4
                        [str_add, pos_add_user_1],#5
                        [str_minus, pos_minus_user_2],#6
                        [str_add, pos_add_user_2], #7
                        [str_minus, pos_minus_user_3],#8
                        [str_add, pos_add_user_3],#9
                        [str_players_number, pos_players_user_number],#10
                        [str_public_cards_number, pos_public_cards_user_number],#11
                        [str_cards_per_person_number, pos_cards_per_person_user_number],#12
                        [str_set, pos_set], #13
                        [str_whole_deck, pos_whole_deck] #14
]

start_display_l = [ [title, pos_title],
                    [str_shuffling, pos_start],
                    [str_current, pos_current_shuffle],
                    [str_current_shuffle_number, pos_current_shuffle_number]
]

deal_display_l = [ [title, pos_title],
                    [str_dealing, pos_start],
                    [str_back, pos_back]
]

####

is_quit = 0
is_stop = 0
is_choose = 0
is_texas_set = 0
is_showhand = 0
is_usr_define = 0
is_set = 0
is_shuffling = 0
is_deal = 0
is_dealt = 0
shuffle_time = int(str_s_time)
mode = "None"
current_shuffle_time = 1

#variables determining the game
players = 1
cards_per_person = 0
public_cards = 0


while is_quit == 0:
    screen.fill(BLACK) # Erase the Work space
    time.sleep(0.005)
    # timeout used for debugging
    t_end = time.time()
    if t_end - t_start >= 1000:
        is_quit = 1
    
    if is_choose == 1:
        for event in pygame.event.get():
            if event.type is MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
            if event.type is MOUSEBUTTONUP:
                pos = pygame.mouse.get_pos()
                # x, y = pos
            if choose_display_l[1][2].collidepoint(pos):
                mode = str_texas
                players = 1
                texas_display_l[2][0] = str(players)
                is_choose = 0
                is_texas_set = 1
            elif choose_display_l[2][2].collidepoint(pos):
                mode = str_FTL
                players = 3
                public_cards = 3
                cards_per_person = 17
                display_l[10][0] = "Players: 3"
                display_l[11][0] = "LL Extra: 3" 
                display_l[12][0] = "Cards: 17" 
                is_choose = 0
            elif choose_display_l[3][2].collidepoint(pos):
                mode = str_showhand
                players = 1
                showhand_display_l[2][0] = str(players)
                is_choose = 0
                is_showhand = 1
            elif choose_display_l[4][2].collidepoint(pos):
                mode = str_usr_define
                is_usr_define = 1
                players = 1
                cards_per_person = 0
                public_cards = 0
                user_define_display_l[10][0] = str(players)
                user_define_display_l[11][0] = str(public_cards)
                user_define_display_l[12][0] = str(cards_per_person)
                is_choose = 0
            elif choose_display_l[5][2].collidepoint(pos):
                mode = str_mode_none
                is_choose = 0
            display_l[7][0] = mode
        choose_display(choose_display_l)
    elif is_texas_set == 1:
        for event in pygame.event.get():
            if event.type is MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
            if event.type is MOUSEBUTTONUP:
                pos = pygame.mouse.get_pos()
                # x, y = pos
                if texas_display_l[4][2].collidepoint(pos):
                    #shuffle time --
                    players = max(1, players -1)
                    texas_display_l[2][0] = str(players)
                if texas_display_l[5][2].collidepoint(pos):
                    #shuffle time ++
                    players = players +1
                    texas_display_l[2][0] = str(players)
                if texas_display_l[3][2].collidepoint(pos):
                    display_l[10][0] = "Players: " + str(players)
                    public_cards = 5
                    cards_per_person = 2
                    display_l[11][0] = "Public: 5" 
                    display_l[12][0] = "Cards: 2" 
                    is_texas_set = 0
        if len(texas_display_l[3]) == 3:
            draw_rect(DARK_BLUE, texas_display_l[3][1], 60, 30, 0)
        texas_display(texas_display_l)
    elif is_showhand == 1:
        for event in pygame.event.get():
            if event.type is MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
            if event.type is MOUSEBUTTONUP:
                pos = pygame.mouse.get_pos()
                # x, y = pos
                if showhand_display_l[4][2].collidepoint(pos):
                    #shuffle time --
                    players = max(1, players -1)
                    showhand_display_l[2][0] = str(players)
                if showhand_display_l[5][2].collidepoint(pos):
                    #shuffle time ++
                    players = players +1
                    showhand_display_l[2][0] = str(players)
                if showhand_display_l[3][2].collidepoint(pos):
                    display_l[10][0] = "Players: " + str(players)
                    public_cards = 0
                    cards_per_person = 3
                    display_l[11][0] = "Public: 0" 
                    display_l[12][0] = "Cards: 3" 
                    is_showhand = 0
        # if len(showhand_display_l[3]) == 3:
        draw_rect(DARK_BLUE, showhand_display_l[3][1], 60, 30, 0)
        texas_display(showhand_display_l)
    elif is_usr_define == 1:
        for event in pygame.event.get():
            if event.type is MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
            if event.type is MOUSEBUTTONUP:
                pos = pygame.mouse.get_pos()
                # x, y = pos
                if user_define_display_l[4][2].collidepoint(pos):
                        #player --
                        players = max(1, players -1)
                        user_define_display_l[10][0] = str(players)
                if user_define_display_l[5][2].collidepoint(pos):
                    #player ++
                    players = players +1
                    user_define_display_l[10][0] = str(players)
                if user_define_display_l[6][2].collidepoint(pos):
                        #public cards --
                        public_cards = max(0, public_cards -1)
                        user_define_display_l[11][0] = str(public_cards)
                if user_define_display_l[7][2].collidepoint(pos):
                    #public cards ++
                    public_cards = public_cards +1
                    user_define_display_l[11][0] = str(public_cards)
                if user_define_display_l[8][2].collidepoint(pos):
                        #cards per person --
                        cards_per_person = max(0, cards_per_person -1)
                        user_define_display_l[12][0] = str(cards_per_person)
                if user_define_display_l[9][2].collidepoint(pos):
                    #cards per person ++
                    cards_per_person = cards_per_person +1
                    user_define_display_l[12][0] = str(cards_per_person)
                if user_define_display_l[13][2].collidepoint(pos):
                    display_l[10][0] = "Players: " + str(players)
                    display_l[11][0] = "Public: " + str(public_cards)
                    display_l[12][0] = "Cards: " + str(cards_per_person)
                    is_usr_define = 0
                if user_define_display_l[14][2].collidepoint(pos):
                    cards_per_person = calculate_max_cards()
                    user_define_display_l[12][0] = str(cards_per_person)

        draw_rect(DARK_BLUE, user_define_display_l[13][1], 60, 30, 0)
        draw_rect(GREEN, user_define_display_l[14][1], 90, 30, 2)
        usr_define_display(user_define_display_l)
    elif is_shuffling == 1:
        current_shuffle_time = 1
        start_display_l[3][0] = str(current_shuffle_time) + rank_string(current_shuffle_time) + " shuffle" 
        for i in range(shuffle_time-1):
            screen.fill(BLACK) # Erase the Work space
            time.sleep(0.005)
            start_display(start_display_l)
            shuffle()
            lift_control()
            current_shuffle_time = current_shuffle_time + 1
            start_display_l[3][0] = str(current_shuffle_time) + rank_string(current_shuffle_time) + " shuffle"
        screen.fill(BLACK) # Erase the Work space
        time.sleep(0.005)
        if shuffle_time >0:
            start_display(start_display_l)
            shuffle()
        is_shuffling = 0
        if mode != "None":
            is_deal = 1
    elif is_deal == 1:
        if is_dealt == 0:
            screen.fill(BLACK) # Erase the Work space
            time.sleep(0.005)
            draw_rect(DARK_BLUE, deal_display_l[2][1], 60, 30, 0)
            start_display(deal_display_l)
            deal(players, public_cards, cards_per_person)
            deal_display_l[1][0] = str_deal_finish
            is_dealt = 1
        elif is_dealt == 1:
            mode = str_mode_none
            display_l[7][0] = mode
            shuffle_time = 2
            display_l[9][0] = str(shuffle_time)
            players = 0
            public_cards = 0
            cards_per_person = 0
            display_l[11][0] = "" 
            display_l[12][0] = "" 
            display_l[10][0] = ""
            for event in pygame.event.get():
                if event.type is MOUSEBUTTONDOWN:
                    pos = pygame.mouse.get_pos()
                if event.type is MOUSEBUTTONUP:
                    pos = pygame.mouse.get_pos()
                    # x, y = pos
                    if deal_display_l[2][2].collidepoint(pos):
                        is_deal = 0
                        is_dealt = 0
                        deal_display_l[1][0] = str_dealing
            draw_rect(DARK_BLUE, deal_display_l[2][1], 60, 30, 0)
            start_display(deal_display_l)
    else:
        for event in pygame.event.get():
            if event.type is MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
            if event.type is MOUSEBUTTONUP:
                pos = pygame.mouse.get_pos()
                # x, y = pos
                if display_l[2][2].collidepoint(pos):
                    #shuffle time --
                    shuffle_time = max(0, shuffle_time -1)
                    display_l[9][0] = str(shuffle_time)
                if display_l[3][2].collidepoint(pos):
                    #shuffle time ++
                    shuffle_time = shuffle_time +1
                    display_l[9][0] = str(shuffle_time)
                if display_l[4][2].collidepoint(pos):
                    is_choose = 1
                if display_l[5][2].collidepoint(pos):
                    is_quit = 1
                if display_l[6][2].collidepoint(pos):
                    is_shuffling = 1

        draw_circle(RED, pos_start, 35)
        # if len(display_l[4]) == 3:
        draw_rect(GREEN, display_l[4][1], 60, 30, 2)
        display(display_l)

pygame.quit()
GPIO.cleanup()